package com.trt.contentcenter.service.content;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.trt.contentcenter.dao.content.MidUserShareMapper;
import com.trt.contentcenter.dao.content.RocketmqTransactionLogMapper;
import com.trt.contentcenter.dao.content.ShareMapper;
import com.trt.contentcenter.domain.dto.content.ShareAuditDTO;
import com.trt.contentcenter.domain.dto.content.ShareDTO;
import com.trt.contentcenter.domain.dto.messaging.UserAddBonusMsgDTO;
import com.trt.contentcenter.domain.dto.user.UserAddBonusDTO;
import com.trt.contentcenter.domain.dto.user.UserDTO;
import com.trt.contentcenter.domain.entity.content.MidUserShare;
import com.trt.contentcenter.domain.entity.content.RocketmqTransactionLog;
import com.trt.contentcenter.domain.entity.content.Share;
import com.trt.contentcenter.domain.enums.AuditStatusEnum;
import com.trt.contentcenter.feignclient.UserCenterFeignClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.rocketmq.spring.support.RocketMQHeaders;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @version 1.0
 * @author: trt
 * @description:
 * @create: 2021-03-21 04:00
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ShareService {

    private final ShareMapper shareMapper;
    private final RestTemplate restTemplate;

    private final DiscoveryClient discoveryClient;

    private final UserCenterFeignClient userCenterFeignClient;

    private final MidUserShareMapper midUserShareMapper;

    // 初始版
    /*public ShareDTO findById(Integer id){
        // 获取分享详情
        Share share = this.shareMapper.selectByPrimaryKey(id);
        // 发布人id
        Integer userId = share.getUserId();
        // 调用微服务的/users/{userId}
        UserDTO userDTO = restTemplate.getForObject(
                "http://localhost:8080/users/{id}",
                UserDTO.class, userId
        );

        ShareDTO shareDTO = new ShareDTO();
        //消息的装配
        BeanUtils.copyProperties(share, shareDTO);
        shareDTO.setWxNickname(userDTO.getWxNickname());

        return shareDTO;
    }*/

    // 第二版 nacos
    /*public ShareDTO findById(Integer id){
        // 获取分享详情
        Share share = this.shareMapper.selectByPrimaryKey(id);
        // 发布人id
        Integer userId = share.getUserId();

        // 用户中心所有实例的信息
        List<ServiceInstance> instances = discoveryClient.getInstances("user-center");
        String targetURL = instances.stream()
                // 数据变换
                .map(instance-> instance.getUri().toString()+ "/users/{id}")
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("当前没有实例!"));

        log.info("请求的目标地址: {}", targetURL);
        // 调用微服务的/users/{userId}
        UserDTO userDTO = restTemplate.getForObject(
                targetURL,
                UserDTO.class, userId
        );

        ShareDTO shareDTO = new ShareDTO();
        //消息的装配
        BeanUtils.copyProperties(share, shareDTO);
        shareDTO.setWxNickname(userDTO.getWxNickname());

        return shareDTO;
    }*/

    /*// 第三版 手写客户端侧负载均衡
    public ShareDTO findById(Integer id){
        // 获取分享详情
        Share share = this.shareMapper.selectByPrimaryKey(id);
        // 发布人id
        Integer userId = share.getUserId();

        // 用户中心所有实例的信息
        List<ServiceInstance> instances = discoveryClient.getInstances("user-center");

        // 所有用户中心实例的请求地址(实现客户端负载均衡)
        List<String> targetURLS = instances.stream()
                // 数据变换
                .map(instance-> instance.getUri().toString()+ "/users/{id}")
                .collect(Collectors.toList());

        // 随机算法
        int i = ThreadLocalRandom.current().nextInt(targetURLS.size());

        String targetURL = targetURLS.get(i);
        log.info("请求的目标地址: {}", targetURL);
        // 调用微服务的/users/{userId}
        UserDTO userDTO = restTemplate.getForObject(
                targetURL,
                UserDTO.class, userId
        );

        ShareDTO shareDTO = new ShareDTO();
        //消息的装配
        BeanUtils.copyProperties(share, shareDTO);
        shareDTO.setWxNickname(userDTO.getWxNickname());

        return shareDTO;
    }*/

   /* // 第三版 Ribbon实现客户端侧负载均衡
    public ShareDTO findById(Integer id){
        // 获取分享详情
        Share share = this.shareMapper.selectByPrimaryKey(id);
        // 发布人id
        Integer userId = share.getUserId();

        // 1.代码不可读
        // 2.复杂的urZ难以维护: https://user-center/s?ie.....
        // 3.难以相应需求的变化，变化很没有幸福感
        // 4.编程体验不统一
        // 调用微服务的/users/{userId}
        UserDTO userDTO = restTemplate.getForObject(
                "http://user-center/users/{userId}",
                UserDTO.class, userId
        );

        ShareDTO shareDTO = new ShareDTO();
        //消息的装配
        BeanUtils.copyProperties(share, shareDTO);
        shareDTO.setWxNickname(userDTO.getWxNickname());

        return shareDTO;
    }*/

   // 第四版 Feign实现客户端侧负载均衡
//   public ShareDTO findById(Integer id, String token){
//       // 获取分享详情
//       Share share = this.shareMapper.selectByPrimaryKey(id);
//       // 发布人id
//       Integer userId = share.getUserId();
//
//       // 调用微服务的/users/{userId}
//       UserDTO userDTO = this.userCenterFeignClient.findById(userId, token);
//
//       ShareDTO shareDTO = new ShareDTO();
//       //消息的装配
//       BeanUtils.copyProperties(share, shareDTO);
//       shareDTO.setWxNickname(userDTO.getWxNickname());
//
//       return shareDTO;
//   }
//
//   private final RocketMQTemplate rocketMQTemplate;

    // Feign传递Token,使用RequestInterceptor
    public ShareDTO findById(Integer id){
        // 获取分享详情
        Share share = this.shareMapper.selectByPrimaryKey(id);
        // 发布人id
        Integer userId = share.getUserId();

        // 调用微服务的/users/{userId}
        UserDTO userDTO = this.userCenterFeignClient.findById(userId);

        ShareDTO shareDTO = new ShareDTO();
        //消息的装配
        BeanUtils.copyProperties(share, shareDTO);
        shareDTO.setWxNickname(userDTO.getWxNickname());

        return shareDTO;
    }

    private final RocketMQTemplate rocketMQTemplate;


    /**
     * MQ适用场景
     * 1.异步处理
     * 2.流量削峰填谷
     * 3.解耦微服务
     */
    // @Transactional(rollbackFor = Exception.class)
//    public Share auditById(Integer id, ShareAuditDTO auditDTO) {
//        // 1.查询share是否存在,不存在或者当前的audit_status != NOT_YET,那么抛异常
//        Share share = this.shareMapper.selectByPrimaryKey(id);
//        if (share == null) {
//            throw new IllegalArgumentException("参数非法！该分享不存在！");
//        }
//        if (!Objects.equals("NOT_YET", share.getAuditStatus())) {
//            throw new IllegalArgumentException("参数非法！该分享已审核通过或审核不通过！");
//        }
//        // 2.审核资源,将状态设为PASS/ REJECT
//        share.setAuditStatus(auditDTO.getAuditStatusEnum().toString());
//        this.shareMapper.updateByPrimaryKey(share);
//        // 3.如果是PASS，那么为发布人添加积分
//        // 异步执行
//        /**
//         * Spring实现异步的方法
//         * 1.AsyncRestTemplate
//         * 参考文档: https://blog.csdn.net/jiangchao858/article/details/86709750
//         * 2.@Async注解
//         * 参考文档:https://spring.io/guides/gs/async-method/
//         * 3.WebClient ( Spring 5.0引入)
//         * 参考文档:https://docs.spring.io/spring/docs/5.1.8.RELEASE/spring-framework-reference/web-reactive.html#webflux-client
//         * 4.MQ
//         */
//        // UserCenterFeignClient.addBonus(id, 500); //耗时，不建议，可采用异步执行
//
//        // 3.如果是PASS，那么发送消息给rocketmq，让用户中心消费，并为发布人添加积分
//        this.rocketMQTemplate.convertAndSend(
//                "add-bonus",
//                UserAddBonusMsgDTO.builder().userId(share.getUserId()).bonus(50).build());
//        return share;
//    }


    // 改造
//    public Share auditById(Integer id, ShareAuditDTO auditDTO) {
//        // 1.查询share是否存在,不存在或者当前的audit_status != NOT_YET,那么抛异常
//        Share share = this.shareMapper.selectByPrimaryKey(id);
//        if (share == null) {
//            throw new IllegalArgumentException("参数非法！该分享不存在！");
//        }
//        if (!Objects.equals("NOT_YET", share.getAuditStatus())) {
//            throw new IllegalArgumentException("参数非法！该分享已审核通过或审核不通过！");
//        }
//
//        // 3.如果是PASS，那么发送消息给rocketmq，让用户中心消费，并为发布人添加积分
//        if (AuditStatusEnum.PASS.equals(auditDTO.getAuditStatusEnum())) {
//            // 发送半消息
//            String transactionId = UUID.randomUUID().toString();
//            this.rocketMQTemplate.sendMessageInTransaction(
//                "tx-add-bonus-group",
//                 "add-bonus",
//                 MessageBuilder
//                    .withPayload(
//                        UserAddBonusMsgDTO.builder()
//                            .userId(share.getUserId())
//                            .bonus(50)
//                            .build()
//                 )
//                  // header也有妙用
//                 .setHeader(RocketMQHeaders.TRANSACTION_ID, transactionId)
//                 .setHeader("share_id", id)
//                 .build(),
//               // arg有大用处
//               auditDTO
//            );
//        }
//        else {
//            this.auditById(id, auditDTO);
//        }
//        return share;
//    }



    // 改造 SpringCloudStream+RocketMQ实现分布式事务
    private final Source source;

    public Share auditById(Integer id, ShareAuditDTO auditDTO) {
        // 1.查询share是否存在,不存在或者当前的audit_status != NOT_YET,那么抛异常
        Share share = this.shareMapper.selectByPrimaryKey(id);
        if (share == null) {
            throw new IllegalArgumentException("参数非法！该分享不存在！");
        }
        if (!Objects.equals("NOT_YET", share.getAuditStatus())) {
            throw new IllegalArgumentException("参数非法！该分享已审核通过或审核不通过！");
        }

        // 3.如果是PASS，那么发送消息给rocketmq，让用户中心消费，并为发布人添加积分
        if (AuditStatusEnum.PASS.equals(auditDTO.getAuditStatusEnum())) {
            // 发送半消息
            String transactionId = UUID.randomUUID().toString();

            this.source.output()
                    .send(
                            MessageBuilder
                                    .withPayload(
                                            UserAddBonusMsgDTO.builder()
                                                    .userId(share.getUserId())
                                                    .bonus(50)
                                                    .build()
                                    )
                                    // header也有妙用
                                    .setHeader(RocketMQHeaders.TRANSACTION_ID, transactionId)
                                    .setHeader("share_id", id)
                                    .setHeader("dto", JSON.toJSONString(auditDTO))
                                    .build()
                    );
        }
        else {
            this.auditById(id, auditDTO);
        }
        return share;
    }

    @Transactional(rollbackFor = Exception.class)
    public void auditByIdInDB(Integer id, ShareAuditDTO auditDTO){
        Share share = Share.builder()
                .id(id)
                .auditStatus(auditDTO.getAuditStatusEnum().toString())
                .reason(auditDTO.getReason())
                .build();
        this.shareMapper.updateByPrimaryKeySelective(share);
        // 4.把share写到缓存
    }


    private final RocketmqTransactionLogMapper rocketMQTransactionLogMapper;


    @Transactional(rollbackFor = Exception.class)
    public void auditByIdWithRocketMqLog(Integer id, ShareAuditDTO auditDTO, String transactionId){
        this.auditByIdInDB(id, auditDTO);
        this.rocketMQTransactionLogMapper.insertSelective(
                RocketmqTransactionLog.builder()
                        .transactionId(transactionId)
                        .log("审核分享...")
                        .build()
        );

    }


    public PageInfo<Share> q(String title, Integer pageNo, Integer pageSize, Integer userId) {
        // 表示我要分页了,它会切入下面这条不分页的SQL, 自动拼接分页的SQL
        PageHelper.startPage(pageNo, pageSize);
        // 部分也的SQL
        List<Share> shares = this.shareMapper.selectByParam(title);

        // 1.如果用户未登录，那么downloadUrl全部设为null
        List<Share> sharesDeal;
        if (userId == null) {
            sharesDeal = shares.stream()
                    .peek(share -> {
                        share.setDownloadUrl(null);
                    })
                    .collect(Collectors.toList());
        }
        // 2.如果用户登录了，那么查询一下mid_user_share，如果没有数据，那么这条share的downloadUrl也设为null
        else {
            sharesDeal = shares.stream()
                    .peek(share -> {
                        MidUserShare midUserShare = this.midUserShareMapper.selectOne(
                                MidUserShare.builder()
                                        .userId(userId)
                                        .shareId(share.getId())
                                        .build()
                        );
                        if (midUserShare == null){
                            share.setDownloadUrl(null);
                        }
                    })
                    .collect(Collectors.toList());
        }

        return new PageInfo<Share>(sharesDeal);
    }

    public Share exchangeById(Integer id, HttpServletRequest request) {
        Object userId = request.getAttribute("id");
        Integer integerUserId = (Integer)userId;

        // 1.根据id查询share,校验是否存在
        Share share = this.shareMapper.selectByPrimaryKey(id);
        if (share == null) {
            throw new IllegalArgumentException("该分享不存在!");
        }
        Integer price = share.getPrice();

        // 2.如果该用户已经兑换过该分享，则直接返回
        MidUserShare midUserShare = this.midUserShareMapper.selectOne(
                MidUserShare.builder()
                        .shareId(id)
                        .userId(integerUserId)
                        .build()
        );
        if (midUserShare != null) {
            return share;
        }

        // 3.根据当前登录的用户id,查询积分是否够
        UserDTO userDTO = this.userCenterFeignClient.findById((Integer) userId);
        if (share.getPrice() > userDTO.getBonus()) {
            throw new IllegalArgumentException("用户积分不够用!");
        }

        // 4.扣减积分 & 往mid_user_share里插入一条数据
        this.userCenterFeignClient.addBonus(
                UserAddBonusDTO.builder()
                        .userId(integerUserId)
                        .bonus(0 - price)
                        .build()
        );

        this.midUserShareMapper.insert(
                MidUserShare.builder()
                        .userId(integerUserId)
                        .shareId(id)
                        .build()
        );

        return share;
    }
}